package util;

import java.io.File;
import java.io.FileWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;



/**
 * @author HP
 * @ClassName AutoCreateBean
 * @Description 实体类生成器
 * @date 2020/5/25 23:10
 */
public class AutoCreateBean {

    //数据库的表名名，可以对应成实体类名
    private static String tableName;
    //列名数组
    private static String [] columnNames;
    //列名类型数组
    private static String [] columnType;
    //列名大小数组
    private static int [] columnSize;
    //是否导入java.util.*包
    private static boolean p_util = false;
    //是否导入java.sql.*包
    private static boolean p_sql = false;




    /**
     * 把输入字符串的首字母改成大写（主要是创建实体类、get/set方法的首字母必须是大写）
     * @param str
     * @return
     */
    private static String initcap(String str) {
        //将传入的字符串分解成字符并存入到数组中
        char [] ch = str.toCharArray();
        //判断字符是否为字母
        if (ch[0] >= 'a' && ch[0] <= 'z') {
            //将首字母强转成大写
            ch[0] = (char) (ch[0] - 32);
        }
        return new String(ch);
    }


    private static String small(String str) {
        //将传入的字符串分解成字符并存入到数组中
        char [] ch = str.toCharArray();
        //判断字符是否为字母
        if (ch[0] >= 'A' && ch[0] <= 'Z') {
            //将首字母强转成大写
            ch[0] = (char) (ch[0] + 32);
        }
        return new String(ch);
    }

    /**
     * 将数据库的数据类型转换成java类的数据类型
     * @return
     */
    private static String sqlTypeGetJavaType(String sqlType) {
        String type = "";
        if (sqlType.equalsIgnoreCase("bit")) {
            type =  "Boolean";
        }
        if (sqlType.equalsIgnoreCase("tinyint")) {
            type =  "Byte";
        }
        if (sqlType.equalsIgnoreCase("smallint")) {
            type =  "Short";
        }
        if (sqlType.equalsIgnoreCase("int")) {
            type =  "Integer";
        }
        if (sqlType.equalsIgnoreCase("bigint")) {
            type =  "Long";
        }
        if (sqlType.equalsIgnoreCase("float")) {
            type =  "Float";
        }
        if (sqlType.equalsIgnoreCase("decimal")
                || sqlType.equalsIgnoreCase("numeric")
                || sqlType.equalsIgnoreCase("real")
                ||sqlType.equalsIgnoreCase("double")) {
            type =  "Double";
        }
        if (sqlType.equalsIgnoreCase("money")
                || sqlType.equalsIgnoreCase("smallmoney")) {
            type =  "Double";
        }
        if (sqlType.equalsIgnoreCase("varchar")
                || sqlType.equalsIgnoreCase("char")
                || sqlType.equalsIgnoreCase("nvarchar")
                || sqlType.equalsIgnoreCase("nchar")
                || sqlType.equalsIgnoreCase("uniqueidentifier")
                || sqlType.equalsIgnoreCase("ntext"))
        {
            type =  "String";
        }
        if (sqlType.equalsIgnoreCase("datetime")
                ||sqlType.equalsIgnoreCase("date")
                || sqlType.equalsIgnoreCase("timestamp")
                || sqlType.equalsIgnoreCase("datetime")){
            type =  "Date";
        }
        if (sqlType.equalsIgnoreCase("image")) {
            type =  "Blob";
        }
        if (sqlType.equalsIgnoreCase("text")) {
            type =  "Clob";
        }

        return type;
    }



    /**
     * 生成属性
     */
    private static void getProperty(StringBuffer sb){
        for (int i = 0; i<columnNames.length;i++){
            sb.append("\tprivate "+sqlTypeGetJavaType(columnType[i])+" "+columnNames[i]+";\r\n");
        }
    }



    /**
     * 生成无参构造、有参构造方法
     * @param sb
     * @param tableName
     */
    private static void getConstructor(StringBuffer sb,String tableName){
        //生成无参构造方法
        sb.append("\tpublic "+tableName+"(){}\r\n");
        //生成有参构造方法
        sb.append("\tpublic "+tableName+"(");
        String link = "";
        for (int i = 0;i<columnNames.length;i++){
            sb.append(link+sqlTypeGetJavaType(columnType[i])+" "+columnNames[i]);
            link = ",";
        }
        sb.append("){\r\n");
        for (int i = 0;i<columnNames.length;i++){
            sb.append("\t\tthis."+columnNames[i]+" = "+columnNames[i]+";\r\n");
        }
        sb.append("\t}\r\n");
        //生成toString
        sb.append("\t@Override\r\n\tpublic String toString() {\r\n");
        sb.append("\treturn ");
        for (int i = 0;i<columnNames.length;i++){
            if (i != columnNames.length-1) {
                sb.append(columnNames[i]+"+\"\\t\""+"+");
            } else {
                sb.append(columnNames[i]+";\r\n}\r\n");
            }
        }
    }





    /**
     * 生成所有属性的get\set方法
     */
    private static void getMethod(StringBuffer sb){
        for (int i = 0;i<columnNames.length;i++){
            //生成set方法 例如：public void setId(int id){ this.id = id}
            sb.append(
        "\n\tpublic void set"+initcap(columnNames[i])+"("+sqlTypeGetJavaType(columnType[i])+" "+
                columnNames[i]+"){\r\n"
            );
            sb.append("\t\tthis."+columnNames[i] +" = "+ columnNames[i]+";\r\n");
            sb.append("\t}\r\n");

            //生成get方法 public String getName(){return name;}
            sb.append("\n\tpublic "+sqlTypeGetJavaType(columnType[i])+" get"+initcap(columnNames[i])+"(){\r\n");
            sb.append("\treturn "+columnNames[i]+";\r\n");
            sb.append("\t}\r\n");
        }
    }




    /**
     * 获取到指定数据中的表
     * @return 返回包含了该数据库中所有表名的一个集合
     */
    public static List<String> TBList(){
        List<String> list = new ArrayList<>();
        ResultSet rs = null;
        Connection conn = null;
        try {
            //连接到数库
            conn = BaseDao.getConnection();
            //获取连接到的数据库的结构信息
            DatabaseMetaData md = conn.getMetaData();
            //通过md.getTables()方法获取到数据库中表的信息 该方法中有四个参数（如下详解）
            //1、String catalog:要获得表所在的所有编目，""表示没有任何目录，null表示所有目录
            //  （.getCatalog()返回当前目录(数据库)名称)
            //2、String schema: 要获得表所在的模式，""表示没有任何模式，null表示所有模式
            //3、String tableName:可以获得与该参数匹配的表名，填null表示所有表，
            //   如果想查找以a开头的表，则是填入"a%",规则和模糊查询同理
            //4、String types:同理null代表所有类型的表，输入类型就只是查到指定类型的表(例如：table、view.....)
            rs = md.getTables(conn.getCatalog(),null,null,null);
            //循环操作结果集
            while(rs.next()){
                //可以使用table_name关键字获取到表的名字
                String tbName = rs.getString("TABLE_NAME");
                //将表名添加到集合中！完成！
                list.add(tbName);
            }
            //关闭资源
            md = null;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            BaseDao.close(rs,null,conn);
        }
        return list;
    }




    /**
     * 生成主体类实体代码
     * @return
     */
    private static String parse(){
        StringBuffer sb = new StringBuffer();
        //判断是否需要导入util.*包
        if (p_util){
            sb.append("import java.util.*;\r\n");
        }

        //判断是否需要导入sql.*包
        if (p_sql){
            sb.append("import java.sql.*;\r\n\r\n");
        }
        //生成实体类代码开始
        sb.append("public class "+initcap(tableName)+"{\r\n");

        //调用生成属性的方法 追加到Stringbuff中
        getProperty(sb);
        //获取属性的封装方法
        getMethod(sb);
        //调用方法获取到构造方法
        getConstructor(sb,initcap(tableName));
        sb.append("\t}\r\n");
        return sb.toString();
    }




    /**
     * 创建java文件 将生成的属性以及封装方法 保存到bean中
     * @param className
     * @param content
     * @param packageName
     */
    public static void markerBean(String className,String content,String packageName){
        //定义文件的保存地址
        String folder = "C:\\Users\\HP\\Desktop\\bean\\";

        File file = new File(folder);
        //若改文件夹不存在则创建该文件夹
        if (!file.exists()){
            file.mkdirs();
        }
        String fielName = folder + initcap(tableName) + ".java";

        try {
            //创建文件
            File newDao = new File(fielName);
            //创建一个写入流
            FileWriter fw = new FileWriter(newDao);
            //写入类头
            fw.write("package\t" + packageName.replace("/", ".") + ";\r\n");
            fw.write(content);
            //启用写入流
            fw.flush();
            //关闭写入
            fw.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }





    /**
     * 最终调用  获得实体类
     * @param TBlist
     * @param packageName
     * @throws Exception
     */
    public static void getEntity(List<String> TBlist, String packageName)throws Exception {
        //获取数据库连接
        Connection conn = BaseDao.getConnection();
        //获取执行对象
        PreparedStatement pstmt = null;
        //获取结果集结构
        ResultSetMetaData rsmd = null;
        //循环操作表
        for (int i = 0; i < TBlist.size(); i++) {
            //获取到表名
            tableName = TBlist.get(i);
            //编写SQL语句
            String strsql = "select * from `" + tableName +"`";
            //获取执行对象
            pstmt = conn.prepareStatement(strsql);
            //获取结果集结构信息
            rsmd = pstmt.getMetaData();
            //获取到列的个数
            int size = rsmd.getColumnCount();
            // 共有多少列
            columnNames = new String[size];
            columnType = new String[size];
            columnSize = new int[size];
            //循环操作表里面的列
            for (int j = 0; j < rsmd.getColumnCount(); j++) {
                //将列名赋值到列名数组中
                columnNames[j] = rsmd.getColumnName(j + 1);
                //将列的数据类型赋值到数组中
                columnType[j] = rsmd.getColumnTypeName(j + 1);
                if (columnType[j].equalsIgnoreCase("datetime")) {
                    p_util = true;
                }
                if (columnType[j].equalsIgnoreCase("image")
                        || columnType[j].equalsIgnoreCase("text")) {
                    p_sql = true;
                }
                //将数据类型长度赋值到数组中
                columnSize[j] = rsmd.getColumnDisplaySize(j + 1);
            }
            //通过实体类生成的方法，生成实体类
            markerBean(initcap(tableName), parse(), packageName);
        }
        System.out.println("实体类生成成功......");
        pstmt.close();
        rsmd = null;
        conn.close();
    }



    /**
     * 根据生成的bean包 获取到项目中的所有的类，然后创建该类的操作接口
     */
    public static void getInterface(){
        //声明要获取的目录的地址
        String folder = "C:\\Users\\HP\\Desktop\\bean\\";
        //获取到该路径下的所有文件的数组
        File file = new File(folder);
        File [] files = file.listFiles();


        //获取到文件名（去后缀）并进行操作
        for (int i = 0;i<files.length;i++){
            //获取文件名（去除后缀）
//            files[i].getAbsolutePath();
            String cName = files[i].getName().substring(0,files[i].getName().indexOf("."));
            //声明生成的接口包的保存地址
            String url = "C:\\Users\\HP\\Desktop\\bean\\";
            String className = url+cName+"Dao.java";
            File intf = new File(url);
            if (!intf.exists()){
                intf.mkdirs();
            }
            String clsName = small(cName);
            //接口代码
            String jk = "public interface "+cName+"Dao {\r\n";
            jk += "\tboolean add("+cName+" "+clsName+");\r\n\r\n\tboolean deleteById(Integer id);\r\n\r\n\t";
            jk += "List<"+cName+"> queryAll();\r\n\r\n\t"+cName+" queryOne(Integer id);";
            jk += "\r\n}";
            try {
                //导包
                String db = "package dao;\r\n\r\nimport java.util.List;\r\n\r\nimport bean.";
                //导包代码完善
                db += cName+";\r\n";
                File ifc = new File(className);
                //创建一个写入流
                FileWriter fw = new FileWriter(ifc);
                //写入导包代码
                fw.write(db);
                //写入接口代码
                fw.write(jk);
                //刷新写入流
                fw.flush();
                //关闭写入流
                fw.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        System.out.println("操作接口创建成功.....");

    }
}
